#ifndef VERTEX_H
#define VERTEX_H

#include <math.h>
#include <iostream>
#include <fstream>

#if defined(__APPLE__) || defined(MACOSX)
	   #include <OpenGL/OpenGL.h>
#endif
#if defined(_WIN32)
	#include <GL/gl.h>
#endif

namespace Y{

#ifndef Loop
	#define Loop for (int i = 0; i < N; ++i)
#endif

template <int N = 3, class D = float>
class Vertex{
	public:
		Vertex(){Loop{intern[i] = 0;}}
		Vertex(D _a){intern[0] = _a;}
		Vertex(D _a, D _b){intern[0] = _a;intern[1] = _b;}
		Vertex(D _a, D _b, D c){intern[0] = _a;intern[1] = _b;intern[2] = c;}
		Vertex(D _a, D _b, D c, D d){intern[0] = _a;intern[1] = _b;intern[2] = c;intern[3] = d;}
		
		Vertex(const Vertex& v){Loop{intern[i] = v.intern[i];}}
		
		D& data (int which){return intern[which];}
		const D data (int which) const {return intern[which];}
		
		Vertex& operator+= (const Vertex& v){Loop{intern[i] += v.intern[i];}return *this;}
		Vertex& operator-= (const Vertex& v){Loop{intern[i] -= v.intern[i];}return *this;}
		Vertex& operator*= (const Vertex& v){Loop{intern[i] *= v.intern[i];}return *this;}
		Vertex& operator/= (const Vertex& v){Loop{intern[i] /= v.intern[i];}return *this;}
		Vertex operator+ (const Vertex& v) const{Vertex _r;Loop{_r.intern[i] = intern[i] + v.intern[i];}return _r;}
		Vertex operator- (const Vertex& v) const{Vertex _r;Loop{_r.intern[i] = intern[i] - v.intern[i];}return _r;}
		Vertex operator* (const Vertex& v) const{Vertex _r;Loop{_r.intern[i] = intern[i] * v.intern[i];}return _r;}
		Vertex operator/ (const Vertex& v) const{Vertex _r;Loop{_r.intern[i] = intern[i] / v.intern[i];}return _r;}
		
		Vertex& operator+= (float f){Loop{intern[i] += f;}return *this;}
		Vertex& operator-= (float f){Loop{intern[i] -= f;}return *this;}
		Vertex& operator*= (float f){Loop{intern[i] *= f;}return *this;}
		Vertex& operator/= (float f){Loop{intern[i] /= f;}return *this;}
		Vertex operator + (float f) const{Vertex _r;Loop{_r.intern[i] = intern[i] + f;}return _r;}
		Vertex operator - (float f) const{Vertex _r;Loop{_r.intern[i] = intern[i] - f;}return _r;}
		Vertex operator * (float f) const{Vertex _r;Loop{_r.intern[i] = intern[i] * f;}return _r;}
		Vertex operator / (float f) const{Vertex _r;Loop{_r.intern[i] = intern[i] / f;}return _r;}
		
		void operator () (D _a){intern[0] = _a;}
		void operator () (D _a, D _b){intern[0] = _a;intern[1] = _b;}
		void operator () (D _a, D _b, D c){intern[0] = _a;intern[1] = _b;intern[2] = c;}
		void operator () (D _a, D _b, D c, D d){intern[0] = _a;intern[1] = _b;intern[2] = c;intern[3] = d;}
		
		void operator () (const Vertex& v){Loop{intern[i] = v.intern[i];}}
		
		bool operator != (const Vertex& v) const{Loop{if (intern[i] != v.intern[i]){return true;}}return false;}
		bool operator == (const Vertex& v) const{Loop{if (intern[i] != v.intern[i]){return false;}}return true;}
		
		D& operator [] (int i){return intern[i];}
		const D operator [] (int i) const {return intern[i];}
		bool isZero () {Loop{if (intern[i] != 0){return false;}}return true;}
		
		inline D& x () {return intern[0];}	inline const D& x () const {return intern[0];}
		inline D& y () {return intern[1];}	inline const D& y () const {return intern[1];}
		inline D& z () {return intern[2];}	inline const D& z () const {return intern[2];}
		
		inline D& r () {return intern[0];}	inline const D& r () const {return intern[0];}
		inline D& g () {return intern[1];}	inline const D& g () const {return intern[1];}
		inline D& b () {return intern[2];}	inline const D& b () const {return intern[2];}
		inline D& a () {return intern[3];}	inline const D& a () const {return intern[3];}
		
		D& first () {return intern[0];}		const D& first () const {return intern[0];}
		D& second () {return intern[1];}	const D& second () const {return intern[1];}
		D& third () {return intern[2];}		const D& third () const {return intern[2];}
		D& fourth () {return intern[3];}	const D& fourth () const {return intern[3];}
		
		D magnitude () {D tmp = 0;Loop{tmp += intern[i] * intern[i];}return sqrt(tmp);}
		Vertex& normalize() { float tmp = 0.0; Loop{tmp += intern[i] * intern[i];} if ( tmp != 0.0 ){ tmp = sqrt(tmp); Loop{intern[i] /= tmp;} }return *this; }
		
		D intern[N];
		
		void serialize (std::fstream f){Loop{f.write(reinterpret_cast<char*>(&intern[i]),sizeof(D));}}

		void print (){std::cout << "(";Loop{std::cout << intern[i];if ( i < N - 1 ){ std::cout << ","; }}std::cout << ")";}
};



typedef Vertex<2,float> Vertex2f;
typedef Vertex<3,float> Vertex3f;
typedef Vertex<4,float> Vertex4f;
typedef Vertex<2,int> Vertex2i;
typedef Vertex<4,unsigned char> Vertex4ub;
typedef Vertex<3,int> Vertex3i;
typedef Vertex<3,double> Vertex3d;
typedef Vertex<3,bool> Vertex3b;

typedef Vertex<2,int> Pair2i;


//GL_C4F_N3F_V3F

struct glVertex_cnv{
	Vertex4f color;
	Vertex3f normal;
	Vertex3f coord;
	
	static GLenum format;
};

//GL_T2F_C4F_N3F_V3F

struct glVertex_tcnv{
	Vertex2f texCoord;
	Vertex4f color;
	Vertex3f normal;
	Vertex3f coord;
	
	static GLenum format;
	
	glVertex_tcnv(){}
	
	glVertex_tcnv( const Vertex2f& tex, const Vertex4f& col, const Vertex3f& nor, const Vertex3f& coo ):
		texCoord(tex),
		color(col),
		normal(nor),
		coord(coo)
	{}
	
	inline glVertex_tcnv operator * (float f){
		glVertex_tcnv ret = *this;
		ret.texCoord *= f;
		ret.color *= f;
		ret.normal *= f;
		ret.coord *= f;
		return ret;
	}
	inline glVertex_tcnv operator - (const glVertex_tcnv& g){
		glVertex_tcnv ret;
		ret.texCoord = texCoord - g.texCoord;
		ret.color = color - g.color;
		ret.normal = normal - g.normal;
		ret.coord = coord - g.coord;
		return ret;
	}
	inline glVertex_tcnv operator + (const glVertex_tcnv& g){
		glVertex_tcnv ret;
		ret.texCoord = texCoord + g.texCoord;
		ret.color = color + g.color;
		ret.normal = normal + g.normal;
		ret.coord = coord + g.coord;
		return ret;
	}
	inline glVertex_tcnv& operator += (const glVertex_tcnv& g){
		texCoord += g.texCoord;
		color += g.color;
		normal += g.normal;
		coord += g.coord;
		return *this;
	}
	inline glVertex_tcnv& operator /= (float f){
		texCoord /= f;
		color /= f;
		normal /= f;
		coord /= f;
		return *this;
	}
};

//GL_C3F_V3F

struct glVertex_cv{
	Vertex3f color;
	Vertex3f coord;
	
	static GLenum format;
};

//GL_T2F_V3F

struct glVertex_tv{
	Vertex2f texCoord;
	Vertex3f coord;
	
	static GLenum format;
};

//GL_T2F_N3F_V3F

struct glVertex_tnv{
	Vertex2f texCoord;
	Vertex3f normal;
	Vertex3f coord;
	
	static GLenum format;
};

//GL_T2F_C4UB_V3F

struct glVertex_tcv{
	Vertex2f texCoord;
	Vertex4ub color;
	Vertex3f coord;
	
	static GLenum format;
};

#undef Loop

}

#endif
